﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Basmus.Conel;

using Microsoft.VisualStudio.TestTools.UnitTesting;
using Ninject;

namespace Basmus.Tests
{
    [TestClass]
    public class SimpleTypesBindingBlackBoxTest
    {
        private IBinder _binder;

        private Binding _binding;

        [TestInitialize]
        public void Initialize()
        {
            _binder = new StandardKernel(new TestNinjectModule()).Get<IBinder>();
        }

        #region simple tests

        [TestMethod]
        public void GettingIntElementsTest()
        {
            //Arrange
            var containerType = typeof(int);
            var containerObjs = new int[] { 0, 1, 4, 6, -1, -100000323 };

            //Act & Assert
            _binding = _binder.Bind(containerType);
            Assert.IsNotNull(_binding);
            Assert.AreEqual(_binding.ElementType, typeof(int));

            //Act & Assert
            foreach (var containerObj in containerObjs)
            {
                var elements = _binding.GetElements(containerObj);

                Assert.IsTrue(elements.Length == 1);
                Assert.IsInstanceOfType(elements[0], typeof(int));
                Assert.IsTrue((int)elements[0] == containerObj);
            }
        }

        [TestMethod]
        public void GettingIntContainerTest()
        {
            //Arrange
            var containerType = typeof(int);
            var elementSets = new[]
                                 {
                                     new object[] {0}, 
                                     new object[0], // empty element list
                                     new object[] {1}, 
                                     new object[] {6}, 
                                     new object[] {-1, 5, 7},
                                     new object[] {-100000323}
                                 };
            var containers = new int[]
                           {
                               0, 
                               0, // bind default value for empty list of elements
                               1, 
                               6, 
                               -1, 
                               -100000323
                           };

            //Act & Assert
            _binding = _binder.Bind(containerType);
            Assert.IsNotNull(_binding);
            Assert.AreEqual(_binding.ElementType, typeof(int));

            //Act & Assert
            for (int i = 0; i < containers.Length; ++i)
            {
                var containerObj = _binding.GetContainer(elementSets[i]);

                Assert.IsInstanceOfType(containerObj, typeof(int));
                var container = (int)containerObj;
                Assert.IsTrue(container == containers[i]);
            }
        }

        /// <summary>
        /// Empty elements lead to container with default value of type (0 for int)
        /// </summary>
        [TestMethod]
        public void GettingIntContainerForEmptyElementsListTest()
        {
            //Arrange
            var containerType = typeof(int);
            var elements = new object[0]; // empty element list

            //Act & Assert
            _binding = _binder.Bind(containerType);
            Assert.IsNotNull(_binding);
            Assert.AreEqual(_binding.ElementType, typeof(int));

            //Act
            var containerObj = _binding.GetContainer(elements);

            //Assert
            Assert.IsInstanceOfType(containerObj, typeof(int));
            Assert.IsTrue((int)containerObj == default(int)); // bind default value for empty list of elements
        }

        [TestMethod]
        public void GettingIntContainerForNullElementsListTest()
        {
            //Arrange
            var containerType = typeof(int);

            //Act & Assert
            _binding = _binder.Bind(containerType);
            Assert.IsNotNull(_binding);
            Assert.AreEqual(_binding.ElementType, typeof(int));

            //Act & Assert
            var container = _binding.GetContainer(null);
            Assert.IsNotNull(container);
            Assert.IsTrue((int)container == default(int));
        }

        #endregion

        public void GettingStringElementsTestHelper()
        {
            //Arrange
            var containerObjs = ElementsHelper.GetElementsOf<string>()
                                              .Where(set => set != null)
                                              .SelectMany(set => set)
                                              .Where(e => !string.IsNullOrEmpty(e))
                                              .ToArray();

            //Act & Assert
            Assert.IsNotNull(_binding);
            Assert.AreEqual(_binding.ElementType, typeof(string));

            //Act & Assert
            foreach (var containerObj in containerObjs)
            {
                var elements = _binding.GetElements(containerObj);

                Assert.IsTrue(elements.Length == 1);
                Assert.IsInstanceOfType(elements[0], typeof(string));
                Assert.IsTrue((string)elements[0] == containerObj);
            }
        }

        public void GettingStringElementsForEmptyStringContainerTestHelper()
        {
            //Act & Assert
            Assert.IsNotNull(_binding);
            Assert.AreEqual(_binding.ElementType, typeof(string));

            //Act & Assert
            var elements = _binding.GetElements(string.Empty);
            Assert.IsNotNull(elements);
            Assert.IsTrue(elements.Length == 0);
        }

        public void GettingStringElementsForNullContainerTestHelper()
        {
            //Act & Assert
            Assert.IsNotNull(_binding);
            Assert.AreEqual(_binding.ElementType, typeof(string));

            //Act
            var elements = _binding.GetElements(null);
            Assert.IsNull(elements);
        }

        public void GettingStringContainerTestHelper()
        {
            //Arrange
            var sets = ElementsHelper.GetElementsOf<string>()
                                            .Where(set => set != null)
                                            .Select(set => set.Where(e => e != null))
                                            .Where(set => set.Any())
                                            .ToArray();

            var elementSets = sets.Select(set => set.Cast<object>().ToArray()).ToArray();

            var containers = sets.Select(set => set.First()).ToArray();

            //Act & Assert
            Assert.IsNotNull(_binding);
            Assert.AreEqual(_binding.ElementType, typeof(string));

            //Act & Assert
            for (int i = 0; i < containers.Length; ++i)
            {
                var containerObj = _binding.GetContainer(elementSets[i]);

                Assert.IsInstanceOfType(containerObj, typeof(string));
                var container = (string)containerObj;
                Assert.IsTrue(container == containers[i]);
            }
        }

        public void GettingStringContainerForEmptyElementsListTestHelper()
        {
            //Arrange
            var elements = new object[0]; // empty element list

            //Act & Assert
            Assert.IsNotNull(_binding);
            Assert.AreEqual(_binding.ElementType, typeof(string));

            //Act
            var containerObj = _binding.GetContainer(elements);

            //Assert
            Assert.IsNotNull(containerObj);
            Assert.IsInstanceOfType(containerObj, typeof(string));
            Assert.AreEqual((string)containerObj, "");
        }

        /// <summary>
        /// Null string element leads to null container
        /// </summary>
        public void GettingStringContainerForFirstNullElementsTestHelper()
        {
            //Arrange
            var masks = ElementsHelper.GetElementsOf<bool>().Where(set => set != null);
            var elementSets = ElementsHelper.GetElementsOf<string>()
                                            .Where(set => set != null)
                                            .SelectMany(set => masks.Select(m => set.Select((e, i) => i < m.Count() && m.ElementAt(i) ? null : (object)e)))
                                            .Select(set => new object[] { null }.Concat(set).ToArray())
                                            .ToArray();

            //Act & Assert
            Assert.IsNotNull(_binding);
            Assert.AreEqual(_binding.ElementType, typeof(string));

            //Act & Assert
            foreach (var elementSet in elementSets)
            {
                var containerObj = _binding.GetContainer(elementSet);

                Assert.IsNotNull(containerObj);
                Assert.IsInstanceOfType(containerObj, typeof(string));
                Assert.AreEqual((string)containerObj, "");
            }
        }

        public void GettingStringContainerForNullElementsListTestHelper()
        {
            //Act & Assert
            Assert.IsNotNull(_binding);
            Assert.AreEqual(_binding.ElementType, typeof(string));

            //Act & Assert
            var container = _binding.GetContainer(null);
            Assert.IsNull(container);
        }

        [TestMethod]
        public void StringContainerTest()
        {
            //Arrange
            var tests = new Action[]
                            {
                                GettingStringElementsTestHelper,
                                GettingStringElementsForEmptyStringContainerTestHelper,
                                GettingStringElementsForNullContainerTestHelper,
                                GettingStringContainerTestHelper,
                                GettingStringContainerForEmptyElementsListTestHelper,
                                GettingStringContainerForFirstNullElementsTestHelper,
                                GettingStringContainerForNullElementsListTestHelper
                            };

            //Act & Assert
            foreach (var test in tests)
            {
                _binding = _binder.Bind(typeof(string));
                test();

                _binding = _binder.Bind(typeof(string), typeof(string));
                test();
            }
        }

        //[TestMethod]
        //public void IncompatibleTypesTest()
        //{
        //    //Arrange
        //    var elementTypes = TypeHelper.EveryPossibleElementTypes();

        //    //Act & Assert
        //    foreach (var elementType in elementTypes)
        //    {
        //        foreach (var incompatibleType in elementTypes.Except(new [] {elementType}))
        //        {
        //            _binding = _binderService.Bind(elementType, incompatibleType);
        //            Assert.IsNull(_binding);    
        //        }

        //        _binding = _binderService.Bind(elementType, elementType);
        //        Assert.IsNotNull(_binding);    
        //    }
        //}

        public void GettingElementsTestHelper<TElement>()
        {
            //Arrange
            var containers = ElementsHelper.GetElementsOf<TElement>()
                                           .Where(set => set != null)
                                           .SelectMany(set => set)
                                           .ToArray();

            //Act & Assert
            Assert.IsNotNull(_binding);
            Assert.AreEqual(_binding.ElementType, typeof(TElement));

            //Act & Assert
            foreach (var container in containers)
            {
                var elements = _binding.GetElements(container);

                Assert.IsTrue(elements.Length == 1);
                Assert.IsInstanceOfType(elements[0], typeof(TElement));
                Assert.AreEqual((TElement)elements[0], container);
            }
        }

        [TestMethod]
        public void GettingElementsTest()
        {
            //Arrange
            var elementTypes = TypeHelper.ValueTypes();

            //Act & Assert
            foreach (var elementType in elementTypes)
            {
                _binding = _binder.Bind(elementType);
                this.Invoke("GettingElementsTestHelper", elementType);

                _binding = _binder.Bind(elementType, elementType);
                this.Invoke("GettingElementsTestHelper", elementType);
            }
        }

        public void GettingElementsForNullContainerTestHelper<TElement>()
        {
            //Act & Assert
            Assert.IsNotNull(_binding);
            Assert.AreEqual(_binding.ElementType, typeof(TElement));

            //Act & Assert
            var elements = _binding.GetElements(null);
            Assert.IsTrue(elements.Length == 0);
        }

        public void GettingContainerTestHelper<TElement>()
        {
            //Arrange
            var sets = ElementsHelper.GetElementsOf<TElement>().Where(set => set != null && set.Any()).ToArray();

            var elementSets = sets.Select(set => set.Cast<object>().ToArray()).ToArray();
            var containers = sets.Select(set => set.First()).ToArray();

            //Act & Assert
            Assert.IsNotNull(_binding);
            Assert.AreEqual(_binding.ElementType, typeof(TElement));

            //Act & Assert
            for (int i = 0; i < containers.Length; ++i)
            {
                var containerObj = _binding.GetContainer(elementSets[i]);

                Assert.AreEqual(containerObj == null, (object)containers[i] == null);
                Assert.AreEqual((TElement)containerObj, containers[i]);
            }
        }

        public void GettingContainerForEmptyElementListTestHelper<TElement>()
        {
            //Act & Assert
            Assert.IsNotNull(_binding);
            Assert.AreEqual(_binding.ElementType, typeof(TElement));

            //Act & Assert
            var containerObj = _binding.GetContainer(new object[] {});

            Assert.AreEqual(containerObj, (object)default(TElement));
            Assert.AreEqual((TElement)containerObj, default(TElement));
        }

        public void GettingContainerForNullElementListTestHelper<TElement>()
        {
            //Act & Assert
            Assert.IsNotNull(_binding);
            Assert.AreEqual(_binding.ElementType, typeof(TElement));

            //Act & Assert
            var containerObj = _binding.GetContainer(null);

            Assert.AreEqual(containerObj, (object)default(TElement));
            Assert.AreEqual((TElement)containerObj, default(TElement));
        }

        [TestMethod]
        public void GettingContainerTest()
        {
            //Arrange
            var tests = new []
                            {
                                "GettingContainerTestHelper",
                                "GettingContainerForEmptyElementListTestHelper",
                                "GettingContainerForNullElementListTestHelper"
                            };
            var elementTypes = TypeHelper.ValueTypes();

            //Act & Assert
            foreach (var elementType in elementTypes)
            {
                foreach (var test in tests)
                {
                    _binding = _binder.Bind(elementType);
                    this.Invoke(test, elementType);

                    _binding = _binder.Bind(elementType, elementType);
                    this.Invoke(test, elementType);
                }
            }
        }
    }
}
